/*
    Author:                 Daniela Gerz cybo18@aol.com
    Author:                 Jannik Mueller jannik-mpunkt@web.de
    Version:                0.1
    copyright / license:    GNU General Public License 3.0
    
    
    What this file is all about:
    This file provides Prolog clauses which enable the java class PlayerLogic
    to solve a Minesweeper game.
    Most clauses (except the helper-clauses) need a Prolog-list representation
    of the game board. Prolog is not keeping track of the board status.
    This is done in Java.
    
    The game board should have the following format:
    [ [Row1][Row2][Row3]... ]
    Each row is a list of all fields in the row.
    The fields can contain the following values:
        * -1:                       The field is unexplored
        * -2:                       The field is marked to contain a mine
        * number between 0 and 8:   Number of neighboured mines.
                                    The field is already explored
                                    
    example of a prolog representation of a 9x9 game-board:
    [[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]]

    an example call is provided for all clauses which are not just auxiliary functions.
    In case the clauses have a wrapper clause, usually only the wrapper is called directly.
    Because of that only the wrapper clauses have a sample call in those cases.
*/


%% remDup(+List, -Erg)
%  Removes duplicate elements from the input list "List".
%  The resulting list is contained in the "Erg" list.
%  Wrapper for the remDup/3 clause.

remDup(List, Erg) :-
    remDup(List, [], Erg).
    
%% remDup(+[], +_, -[])
%% remDup(+[H|T], +Akk, -Erg)
%% remDup(+[H|T], +Akk, -[H|Erg])
%  See remDup/2 for purpose.
%  An additional temp-parameter is needed.

remDup([], _, []) :-
    % In case the input list is empty, the resulting list is empty too.
    !.

remDup([H|T], Akk, Erg) :-
    % remDup is called without the head element H, if it is already
    % contained in the "Akk"-list
    member(H, Akk),
    remDup(T, Akk, Erg).
    
remDup([H|T], Akk, [H|Erg]) :-
    % H is added to the "Akk list, if it is not already contained in it.
    \+member(H, Akk),
    !,
    remDup(T, [H|Akk], Erg).
    
    

%% getLength(+[], -0)
%% getLength(+[H|[]], -1)
%% getLength(+[H|L], -Result)
%  Calculates the length of the input list (first argument).
%  Second parameter: List length

getLength([], 0):-
    % In case of an empty list, length is 0.
    !.

getLength([H|[]], 1):-
    % In case the list has only a head element, length is 1.
    !.

getLength([H|L], Result):-
    % Call getLength recursively, removing the head element from the list.
    % length is length + 1.
    getLength(L, R), 
    Result is R+1.
    
    
    
%% getElement(+Number, +[Head|L], -Result)
%  Get the specified element of the list.
%  Number:   Position of the element in the list
%  [Head|L]: List
%  Result:   The element at position "Number"


getElement(Number, [Head|L], Result):-
	( Number =< 0
	-> Number is 1),
	getElement(Number, [Head|L], Result).

getElement(Number, [Head|L], Result):-
    % If number is 1, the specified element is contained in "H".
    Number is 1, 
    Result = Head.
    
getElement(Number, [Head|L], Result):-
    % Number is not 1,
    % element has not yet been found.
    NewNumber is Number-1, 
    getElement(NewNumber, L, Result).
    
    
    
%% isUnexplored(+X, +Y, +Brett, -Result)
%  Checks whether a specified field is unexplored or not.
%  X:        X coordinate of the field
%  Y:        Y coordinate of the field
%  Brett:    The prolog representation of the game board
%  Result:   Empty list if the field is explored.
%            [X, Y] if the field is not explored.
% example call: isUnexplored(2,1,[[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

isUnexplored(X, Y, Brett, Result):-
    % The field is not explored yet
    getElement(Y, Brett, Row), 
    getElement(X, Row, Field), 
    Field is -1, 
    Result = [X, Y].

isUnexplored(X, Y, Brett, Result):-
    % The field is already explored or is flagged to contain a mine
    getElement(Y, Brett, Row), 
    getElement(X, Row, Field), 
    Field \= -1, 
    Result = [].
    


%% getFieldContent(+X, +Y, +Brett, -Field)
%  Gets the number stored in a specified field.
%  X:        X coordinate of the field
%  Y:        Y coordinate of the field
%  Brett:    The prolog representation of the game board
%  Field:    The contained number at X, Y
%  example call: getFieldContent(2,1,[[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Field).

getFieldContent(X, Y, Brett, Field):-
    getElement(Y, Brett, Row), 
    getElement(X, Row, Field).



%% deleteEmptyLists(+[H|[]], -Result)
%% deleteEmptyLists(+[H|L], -Result)
%% deleteEmptyLists(+[H|L], -[H|Result])
%  Removes all empty lists from a list of list.
%  First argument contains a list with empty lists,
%  The second one (Result) contains at the end the list
%  without empty lists.

deleteEmptyLists([H|[]], Result):-
    % List contains two empty lists
    H = [[]], 
    Result = [].
    
deleteEmptyLists([H|[]], Result):-
    % List contains H, which is no empty list as last element.
    L = [], 
    Result = H.

deleteEmptyLists([H|L], Result):-
    % H is an empty list.
    % call clause recursively without H and without adding
    % H to the Result list.
    H = [], 
    deleteEmptyLists(L, Result).

deleteEmptyLists([H|L], Result):-
    % H is an empty list.
    % call clause recursively without H and without adding
    % H to the Result list.
    H = ['[]'], 
    deleteEmptyLists(L, Result).
    
deleteEmptyLists([H|L], [H|Result]):-
    % H is an empty list.
    % call clause recursively without H, adding
    % H to the Result list.
    deleteEmptyLists(L, Result).
    


%% getUnexploredFields(+MitteX, +MitteY, +Brett, -NewResult)
%  Gets the coordinates of all unexplored fields from the 8 neighboured
%  fields.
%  MitteX:       X coordinate of the field in the middle
%  MiiteY:       Y coordinate of the field in the middle
%  Brett:        The prolog representation of the game board
%  NewResult:    List of the coordinates of all neighboured
%                unexplored fields
%  Wrapper for the getUnexploredFields/8 clause
%  example call: getUnexploredFields(2,1,[[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

getUnexploredFields(MitteX, MitteY, Brett, NewResult):-
    X is MitteX-1, 
    Y is MitteY-1, 
    getElement(1, Brett, Row), 
    length(Row, GrenzX), 
    length(Brett, GrenzY), 
    getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result), 
    deleteEmptyLists(Result, NewResult).

%% getUnexploredFields(+X, +Y,  +MitteX, +MitteY, +GrenzX, +GrenzY, +Brett, -[NewR|Result])
%% getUnexploredFields(+X, +Y,  +MitteX, +MitteY, +GrenzX, +GrenzY, +Brett, -Result)
%  See getUnexploredFields/4 for purpose.
%  Additional parameters:
%  X, Y:             The coordinates of the neighboured field which should be
%                    processed next
%  GrenzX, GrenzY:   The border of the game board (maximum X, Y coordinates)

getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, [NewR|Result]):-
    % Case: X has to be incremented to get to the next field.
    X > 0, 
    X < MitteX+1, 
    X =< GrenzX, 
    Y > 0, 
    isUnexplored(X, Y, Brett, NewR), 
    NewX is X+1, 
    getUnexploredFields(NewX, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result).
    
getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, [NewR|Result]):-
    % Case: Y has to be incremented and X has to be set to MitteX-1 ( new row )
    Y > 0, 
    X > 0, 
    X is MitteX+1, 
    Y < MitteY+1, 
    isUnexplored(X, Y, Brett, NewR), 
    NewX is MitteX-1, 
    NewY is Y+1, 
    getUnexploredFields(NewX, NewY,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result).
    
getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result):-
    % X has somehow become 0 or negative.
    % Set X to the lowest legal x-coordinate
    % This could happen if MitteX is 1
    Y > 0, 
    X =< 0, 
    NewX is 1, 
    getUnexploredFields(NewX, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result).
    
getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result):-
    % Y has somehow become 0 or negative.
    % Set Y to the lowest legal y-coordinate
    % This could be happen if MitteY is 1
    Y =< 0, 
    NewY is 1, 
    getUnexploredFields(X, NewY,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result).
    
getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result):-
    % X is greater than the width of the game board
    % This could happen if MitteX is GrenzX
    X > GrenzX, 
    Y < MitteY+1, 
    NewX is MitteX-1, 
    NewY is Y+1, 
    getUnexploredFields(NewX, NewY,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result).
    
getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result):-
    % Y is greater than GrenzY
    % This can happen if MitteY is GrenzY
    Y > GrenzY, 
    Result = [[]].
    
getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result):-
    % X, Y is the last neighbour to process
    X is MitteX+1, 
    Y is MitteY+1, 
    isUnexplored(X, Y, Brett, NewR), 
    Result = [[NewR]].
    
getUnexploredFields(X, Y,  MitteX, MitteY, GrenzX, GrenzY, Brett, Result):-
    % X, Y is the last neighbour to process and
    % MitteX was GrenzX
    X is GrenzX, 
    Y is MitteY+1, 
    isUnexplored(X, Y, Brett, NewR), 
    Result = [[NewR]].



%% isToFlag(+X, +Y, +Brett, -Result)
%  Checks if the number of the neighboured mines of X, Y equals
%  the number of unexplored neighboured fields.
%  In this case all this unexplored fields can be flagged.
%  X:       X coordinate of the field
%  Y:       Y coordinate of the field
%  Brett:   The prolog representation of the game board
%  Result:  List of the coordinates of the neighboured fields
%           that could be flagged
% example call: isToFlag(2,1,[[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

isToFlag(X, Y, Brett, Result):-
    % number of neighboured unexplored fields equals the number of
    % neighboured mines
    getFieldContent(X, Y, Brett, R), 
    R > 0, 
    once(getUnexploredFields(X, Y, Brett, L)), 
    getLength(L, B), 
    R =:= B, 
    Result = L.
    
isToFlag(X, Y, Brett, Result):-
    % number of neighboured unexplored fields does not equal the number of
    % neighboured mines
    Result = [].



%% getFieldsToFlag(+Brett, -NewResultRemDup)
%  Returns all Fields on the prolog board, which are recognized to be a bomb.
%  Therefore the clauses process all fields of the game board.
%  Brett:             The prolog representation of the game board
%  NewResultRemDup:   List of the coordinates of all fields that should be flagged.
%  Wrapper for the getFieldsToFlag/6
%  example call: getFieldsToFlag([[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

getFieldsToFlag(Brett, NewResultRemDup):-
    X is 1, 
    Y is 1, 
    getElement(1, Brett, Row), 
    length(Row, GrenzX), 
    length(Brett, GrenzY), 
    getFieldsToFlag(X, Y, GrenzX, GrenzY, Brett, Result), 
    deleteEmptyLists(Result, NewResult), 
    remDup(NewResult, NewResultRemDup).

%% getFieldsToFlag(+X, +Y, +GrenzX, +GrenzY, +Brett, -NewResult)
%  See getFieldsToFlag/2 for purpose
%  Additional parameters:
%  X, Y:             Coordinates of the field which should be processed next
%  GrenzX, GrenzY:   Border of the game board (as max. coordinates)

getFieldsToFlag(X, Y, GrenzX, GrenzY, Brett, NewResult):-
    % Set X to X+1 (Step one field to the right)
    X < GrenzX, 
    Y =< GrenzY, 
    isToFlag(X, Y, Brett, R), !, 
    NewX is X + 1, 
    getFieldsToFlag(NewX, Y, GrenzX, GrenzY, Brett, Result), 
    append(R, Result, NewResult).
    
getFieldsToFlag(X, Y, GrenzX, GrenzY, Brett, NewResult):-
    % End of line (X). Increase Y
    X = GrenzX, 
    Y < GrenzY, 
    isToFlag(X, Y, Brett, R), !, 
    NewX is 1, 
    NewY is Y + 1, 
    getFieldsToFlag(NewX, NewY, GrenzX, GrenzY, Brett, Result), 
    append(R, Result, NewResult).

getFieldsToFlag(X, Y, GrenzX, GrenzY, Brett, Result):-
    % Last field to process. Do not increase X or Y,
    % abort after processing this field
    X = GrenzX, 
    Y = GrenzY, 
    isToFlag(X, Y, Brett, R), !, 
    Result = [[R]].



%% canBeExplored(+X, +Y, +Brett, -Result)
%  Checks whether the neighboured fields of X, Y
%  can be explored (the number of neighboured mines is 0,
%  the number of neighboured unexplored fields is not 0).
%  X:       X coordinate of the field
%  Y:       Y coordinate of the field
%  Brett:   The prolog representation of the game board
%  Result:  Coordinates of the cells which can be safely explored or
%           [] if there are none.
%  example call: canBeExplored(2,1,[[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

canBeExplored(X, Y, Brett, Result):-
    % neighboured fields do not contain a mine
    getFieldContent(X, Y, Brett, R), 
    R = 0, 
    once(getUnexploredFields(X, Y, Brett, L)),
    Result = L.
    
canBeExplored(X, Y, Brett, Result):-
    % Neighboured fields do contain a mine or
    % the field is unexplored / flagged
    Result = [].



%% getFieldsToExplore(+Brett, -NewResultRemDup)
%  Returns all fields of the game board wich can be safely explored
%  Therefore it processes all fields.
%  Brett:           The prolog representation of the game board
%  NewResultRemDup: Coordinates of all fields of the game board
%                   wich can be safely explored
%  Wrapper for the getFieldsToExplore/6 clause
%  example call: getFieldsToExplore([[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

getFieldsToExplore(Brett, NewResultRemDup):-
    X is 1, 
    Y is 1, 
    getElement(1, Brett, Row), 
    length(Row, GrenzX), 
    length(Brett, GrenzY), 
    getFieldsToExplore(X, Y, GrenzX, GrenzY, Brett, Result), 
    deleteEmptyLists(Result, NewResult), 
    remDup(NewResult, NewResultRemDup).



%% getFieldsToExplore(+X, +Y, +GrenzX, +GrenzY, +Brett, -NewResult)
%% getFieldsToExplore(+X, +Y, +GrenzX, +GrenzY, +Brett, -Result)
%  For purpose see getFieldsToExplore/2
%  Additional parameters:
%  X, Y:            Coordinates of the current field to process
%  GrenzX, GrenzY:   Border of the game board (as max. coordinates)

getFieldsToExplore(X, Y, GrenzX, GrenzY, Brett, NewResult):-
    % Increase X (go one step to the right)
    X < GrenzX, 
    Y =< GrenzY, 
    canBeExplored(X, Y, Brett, R), 
    NewX is X + 1, 
    getFieldsToExplore(NewX, Y, GrenzX, GrenzY, Brett, Result), 
    append(R, Result, NewResult).
    
getFieldsToExplore(X, Y, GrenzX, GrenzY, Brett, NewResult):-
    % "new line": increase Y, reset X
    X = GrenzX, 
    Y < GrenzY, 
    canBeExplored(X, Y, Brett, R), 
    NewX is 1, 
    NewY is Y + 1, 
    getFieldsToExplore(NewX, NewY, GrenzX, GrenzY, Brett, Result), 
    append(R, Result, NewResult).

getFieldsToExplore(X, Y, GrenzX, GrenzY, Brett, Result):-
    % field X, Y is last field to process.
    X = GrenzX, 
    Y = GrenzY, 
    canBeExplored(X, Y, Brett, R), 
    Result = [[R]].



%% getAllUnexploredFields(+Brett, -NewResultRemDup)
%  get a list of all unexplored fields on the whole game board.
%  Brett:           The prolog representation of the game board
%  NewResultRemDup: Coordinates of all fields of the game board
%                   wich are currently unexplored
%  Wrapper for getAllUnexploredFields/4
%  example call: getAllUnexploredFields([[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

getAllUnexploredFields(Brett, NewResultRemDup):-
    X is 1, 
    Y is 1, 
    getElement(1, Brett, Row), 
    length(Row, GrenzX), 
    length(Brett, GrenzY), 
    getAllUnexploredFields(X, Y, GrenzX, GrenzY, Brett, Result), 
    deleteEmptyLists(Result, NewResult), 
    remDup(NewResult, NewResultRemDup).



%% getAllUnexploredFields(+X, +Y, +GrenzX, +GrenzY, +Brett, -[R|Result])
%% getAllUnexploredFields(+X, +Y, +GrenzX, +GrenzY, +Brett, -Result)
%  For purpose see getAllUnexploredFields/4
%  Additional parameters:
%  X, Y:            Coordinates of the current field to process
%  GrenzX, GrenzY:   Border of the game board (as max. coordinates)

getAllUnexploredFields(X, Y, GrenzX, GrenzY, Brett, [R|Result]):-
    % increase X (go one step right)
    X < GrenzX, 
    Y =< GrenzY, 
    isUnexplored(X, Y, Brett, R), 
    NewX is X + 1, 
    getAllUnexploredFields(NewX, Y, GrenzX, GrenzY, Brett, Result).
    
getAllUnexploredFields(X, Y, GrenzX, GrenzY, Brett, [R|Result]):-
    % X is GrenzX, increase Y and reset X
    X = GrenzX, 
    Y < GrenzY, 
    isUnexplored(X, Y, Brett, R), 
    NewX is 1, 
    NewY is Y + 1, 
    getAllUnexploredFields(NewX, NewY, GrenzX, GrenzY, Brett, Result).

getAllUnexploredFields(X, Y, GrenzX, GrenzY, Brett, Result):-
    % field X, Y is last field to process
    X = GrenzX, 
    Y = GrenzY, 
    isUnexplored(X, Y, Brett, R), 
    Result = [[R]].



%% getRandomUnexploredField(+Brett, -RandField)
%  Returns the coordinates of a random unexplored fields on the game board
%  Brett:           The prolog representation of the game board
%  RandField:       Coordinates of a random unexplored field
%  example call: getRandomUnexploredField([[-1,-1,1,0,0,0,0,0,0],[2,2,1,0,0,0,0,1,1],[0,0,0,0,1,2,2,2,-1],[0,0,0,0,1,-1,-1,-1,-1],[0,0,0,1,2,-1,-1,-1,-1],[0,0,0,1,-1,-1,-1,-1,-1],[1,1,0,1,1,1,1,-1,-1],[-1,2,1,0,0,0,2,-1,-1],[-1,-1,1,0,0,0,1,-1,-1]], Result).

getRandomUnexploredField(Brett, RandField):-
    once(getAllUnexploredFields(Brett, Result)), 
    getLength(Result, L),
    %...since the random number is < L
    RandInt is random(L) + 1,
    getElement(RandInt, Result, RandField).

